package cn.lizemin.web;

import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import cn.lizemin.common.RespBean;
import cn.lizemin.web.common.Handler;
import cn.lizemin.web.common.HandlerMapping;
import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Objects;

/**
 * @author lzm
 * @Date 2024/7/20
 * @description 处理请求
 */
public class DispatcherServletV3 extends HttpServlet {

    private final HandlerMapping handlerMapping = new HandlerMapping();

    /**
     * 在处理请求前被调用，在这里加载springmvc配置文件
     * 这个方法一生只会执行一次，在servlet被创建时执行
     */
    @SneakyThrows
    @Override
    public void init() {
        String location = getServletConfig().getInitParameter("configLocation");
        InputStream is = getClass().getClassLoader().getResourceAsStream(location);
        SAXReader reader = new SAXReader();
        Document doc = reader.read(is);
        Element root = doc.getRootElement();
        ArrayList<Object> beans = new ArrayList<>();
        for (Element element : root.elements()) {
            String clazz = element.attributeValue("class");
            System.out.println(clazz);
            Object bean = Class.forName(clazz).newInstance();
            beans.add(bean);
        }
        handlerMapping.process(beans);
    }

    /**
     * 每次servlet被访问，都会执行这个方法
     */
    @SneakyThrows
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws RuntimeException {
        String uri = req.getRequestURI();
        System.out.println(String.format("uri: %s", uri));
        String contextPath = req.getContextPath();
        Handler handler = handlerMapping.getHandler(uri.substring(contextPath.length()));
        if (Objects.isNull(handler)) {
            output(resp,JSONUtil.toJsonStr(RespBean.fail()));
            return;
        }
        Method method = handler.getMethod();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object result;
        // 实现了对有请求参数的接口的调用处理
        if (parameterTypes.length > 0) {
            Enumeration<String> parameterNames = req.getParameterNames();
            ArrayList<Object> paramValues = new ArrayList<>();
            while (parameterNames.hasMoreElements()) {
                String name = parameterNames.nextElement();
                String valule = req.getParameter(name);
                // 我这里只会用到Integer类型的入参，先不搞那么复杂
                paramValues.add(Integer.valueOf(valule));
            }
            result = method.invoke(handler.getObject(), paramValues.toArray());
        } else {
            result = method.invoke(handler.getObject());
        }
        System.out.println(String.format("result: %s", result));
        String jsonStr = JSONUtil.toJsonStr(result);
        output(resp, jsonStr);
        System.out.println("do service");
    }

    private static void output(HttpServletResponse resp, String jsonStr) throws IOException {
        resp.setContentType(ContentType.JSON.getValue());
        resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
        PrintWriter writer = resp.getWriter();
        writer.println(jsonStr);
    }

}
